/*! \file synapse.h
    \brief Shared memory utility classes.
	
	'Synapse' Data Sharing System | Flinders University | Martin Luerssen | 2009+
	--------- Last update: 1/12/10
*/

#ifndef _SYNAPSE_H
#define _SYNAPSE_H

#include <windows.h>

//-----------------------------------------------------------------------------

typedef long long SynapticInfoFlag;
const SynapticInfoFlag SYNAPSE_NOFLAG = 0x00000000;
const SynapticInfoFlag SYNAPSE_STOPWAITING = 0x00000001;

//---------

//! Synapse parameter structure.
struct SynapticInfo
{
	//! Default constructor.
	SynapticInfo() 
	: lastReadTime(0),
	  lastWriteTime(0),
	  flags(SYNAPSE_NOFLAG)
	{
	}

	//! Time of last successful read operation.
	long long lastReadTime;
	//! Time of last successful write operation.
	long long lastWriteTime;
	//! Additional status bits.
	SynapticInfoFlag flags;

};

//---------

//! Parameters of shared Synapse file.
struct SharedSynapticInfo : public SynapticInfo
{
	//! Default constructor.
	SharedSynapticInfo()
	: currentReaders(0),
	  arraySize(0),
	  unitSize(0),
	  waitingReaders(0),
	  waitingWriters(0)
	{
	}

	//! Number of concurrent Synapses accessing this data at this time.
	long currentReaders;
	//! Number of data object instances contained in file.
	long arraySize;
	//! Effective memory size of each object instance.
	long unitSize;
	//! Current number of Synapses waiting to read.
	long waitingReaders;
	//! Current number of Synapses waiting to write.
	long waitingWriters;
};

//-----------------------------------------------------------------------------

//! Reference to an array or element in an array.
template <class T>
struct SynapticReference
{
	SynapticReference(const T& a_rObject, long a_Index);
	SynapticReference(const T* a_pArrayIn, long a_Size);

	//! Pointer to data.
	T* pData;
	//! Index or size of array (depending on context).
	long number;
	//! Type of reference (1 = single element; 2 = array).
	long type;
};

//---------

//! Constructor for reference to array element.
/*!
 * \param a_rObject Object of which to construct a reference. 
 * \param a_Index Index of object in some known array.
 */
template <class T>
inline
SynapticReference<T>::SynapticReference(const T& a_rObject, long a_Index)
: pData(const_cast<T*>(&a_rObject)),
  number(a_Index),
  type(1)
{
}

//! Constructor for reference to array.
/*!
 * \param a_pArrayIn Array for which to construct a reference. 
 * \param a_Size Size of this array.
 */
template <class T>
inline
SynapticReference<T>::SynapticReference(const T* a_pArrayIn, long a_Size)
: pData(const_cast<T*>(a_pArrayIn)),
  number(a_Size),
  type(2)
{
}

//-----------------------------------------------------------------------------

//! Memory-mapped file storage for object/primitive arrays.
template <class T>
class Synapse
{
public:
	Synapse();
	Synapse(const Synapse<T>& a_rOther);
	virtual ~Synapse();

	Synapse<T>& operator=(const Synapse<T>& a_rOther);
	T& Grab(long a_Index = 0);
	T GrabCopy(long a_Index = 0);
	T* GrabPointer();
	void FillPointer(const T* a_pOther, long a_Size = LONG_MAX);
	long Size();

	bool LockNow();
	void Lock();
	bool WriteLock();
	void Unlock();
	
	void SignalRead();
	void SignalWrite();
	bool WaitForRead();
	bool WaitForWrite();
	bool WaitForNew();
	bool WaitForOld();
	bool WaitForMatch(T a_rObject, long a_Index = 0, bool a_fComparator(const T& a_rTarget, const T& a_rValue) = Synapse::Equal);
	void StopWaiting(bool a_StopValue, bool a_StopEveryone = false);
	bool HasStoppedWaiting();

	bool IsNew();
	bool IsOld();
	static long long Time();
	static long long OneSecond();
	void SetAsRead(long long a_NewTime = Time());
	void SetAsWritten(long long a_NewTime = Time());

	static bool Exists(const char* a_pShareName);
	bool Create(const char* a_pShareName, long a_Size = 1, const T& a_rDefaultObject = T());
	bool CreateFromArray(const char* a_pShareName, long a_Size, const T a_pArrayIn[]);
	bool OpenNow(const char* a_pShareName);
	bool Open(const char* a_pShareName, long a_PollWaitTime = 50);
	void Release();
	bool IsOpen() const;
	const char* GetName() const;
		
	bool WriteNow(const T& a_rObject, long a_Index = 0);
	bool ReadNow(T& a_rObject, long a_Index = 0);
	T ReadCopyNow(long a_Index = 0);
	bool Write(const T& a_rObject, long a_Index = 0);
	bool ProtectWrite(const T& a_rObject, long a_Index = 0);
	bool Read(T& a_rObject, long a_Index = 0);
	T ReadCopy(long a_Index = 0);
	bool WriteWhenOld(const T& a_rObject, long a_Index = 0);
	bool ReadWhenNew(T& a_rObject, long a_Index = 0);
	T ReadCopyWhenNew(long a_Index = 0);

	bool WriteArrayNow(const T a_pArrayIn[], long a_Size = LONG_MAX);
	bool ReadArrayNow(T a_pArrayOut[], long a_Size = LONG_MAX);
	bool WriteArray(const T a_pArrayIn[], long a_Size = LONG_MAX);
	bool ProtectWriteArray(const T a_pArrayIn[], long a_Size = LONG_MAX);
	bool ReadArray(T a_pArrayOut[], long a_Size = LONG_MAX);
	bool WriteArrayWhenOld(const T a_pArrayIn[], long a_Size = LONG_MAX);
	bool ReadArrayWhenNew(T a_pArrayOut[], long a_Size = LONG_MAX);

	SynapticInfo& LocalInfo();
	SharedSynapticInfo& SharedInfo();

	//! Equality comparison function.
	static bool Equal(const T& a_rTarget, const T& a_rValue) { return (a_rTarget == a_rValue); }
	//! Inequality comparison function.
	static bool NotEqual(const T& a_rTarget, const T& a_rValue) { return (a_rTarget != a_rValue); }
	//! >= comparison function.
	static bool GreaterThanEqual(const T& a_rTarget, const T& a_rValue) { return (a_rTarget >= a_rValue); }
	//! > comparison function.
	static bool GreaterThan(const T& a_rTarget, const T& a_rValue) { return (a_rTarget > a_rValue); }
	//! <= comparison function.
	static bool LessThanEqual(const T& a_rTarget, const T& a_rValue) { return (a_rTarget <= a_rValue); }
	//! < comparison function.
	static bool LessThan(const T& a_rTarget, const T& a_rValue) { return (a_rTarget < a_rValue); }

protected:
	bool _Create(const char* a_pShareName, const SynapticReference<T>& a_rSR);
	bool _WriteNow(const SynapticReference<T>& a_rSR);
	bool _ReadNow(const SynapticReference<T>& a_rSR);
	bool _Write(const SynapticReference<T>& a_rSR);
	bool _ProtectWrite(const SynapticReference<T>& a_rSR);
	void _Read(const SynapticReference<T>& a_rSR);
	bool _WriteWhenOld(const SynapticReference<T>& a_rSR);
	bool _ReadWhenNew(const SynapticReference<T>& a_rSR);
	void _CopyToShared(const SynapticReference<T>& a_rSR);
	void _CopyFromShared(const SynapticReference<T>& a_rSR);
	void _UpdateReadTime(long long a_NewTime);
	void _UpdateWriteTime(long long a_NewTime);
	bool _IsNew();
	bool _IsOld();

	//! Name of shared object.
	char* m_pShareName;
	//! Number of data object instances.
	long m_Size;
	//! Mutex object.
	HANDLE m_hMutex;
	//! Read event object.
	HANDLE m_hReadEvent;
	//! Write event object.
	HANDLE m_hWriteEvent;
	//! Number of entered mutex locks.
	long m_LockCount;
	//! File mapping object handle.
	HANDLE m_hMapFile;
	//! File mapping view.
	void* m_pMap;
	//! Synapse parameters.
	SynapticInfo m_Info;
};

//---------

//! Default constructor.
template <class T>
Synapse<T>::Synapse()
: m_pShareName(NULL),
  m_Size(0),
  m_hMutex(0),
  m_hReadEvent(0),
  m_hWriteEvent(0),
  m_LockCount(0),
  m_hMapFile(0),
  m_pMap(NULL)
{
}

//! Copy constructor.
/*!
 * \param a_rOther Another Synapse from which to copy data. 
 */
template <class T>
Synapse<T>::Synapse(const Synapse<T>& a_rOther)
: m_pShareName(NULL),
  m_Size(0),
  m_hMutex(0),
  m_hReadEvent(0),
  m_hWriteEvent(0),
  m_LockCount(0),
  m_hMapFile(0),
  m_pMap(NULL),
  m_Info(a_rOther.m_Info)
{
	if (a_rOther.IsOpen())
	{
		Open(a_rOther.GetName());
	}
}

//! Destructor.
template <class T>
Synapse<T>::~Synapse()
{
	Release();
}

//---------

//! Assignment operator.
/*!
 * \param a_rOther Another Synapse from which to assign data. 
 * \return The updated Synapse.
 */
template <class T>
Synapse<T>& Synapse<T>::operator=(const Synapse<T>& a_rOther)
{
	m_Info = a_rOther.m_Info;
	if (a_rOther.IsOpen())
	{
		Open(a_rOther.GetName());
	}
	else
	{
		Release();
	}
	return *this;
}

//! Direct access to the contents of the memory-mapped file (returns object reference).
/*! 
 * Not thread safe.
 * \param a_Index Index of the object to return (if multiple in file). 
 * \return Properly typed reference to the included object.
 */
template <class T>
inline
T& Synapse<T>::Grab(long a_Index)
{
	return *(reinterpret_cast<T*>(static_cast<char*>(m_pMap) 
		     + sizeof(SharedSynapticInfo)
			 + a_Index * sizeof(T)));
}

//! Direct access to the contents of the memory-mapped file (returns object copy).
/*! 
 * Not thread safe.
 * \param a_Index Index of the object to return (if multiple in file).
 * \return Copy of the included object.
 */
template <class T>
inline
T Synapse<T>::GrabCopy(long a_Index)
{
	return *(reinterpret_cast<T*>(static_cast<char*>(m_pMap) 
		     + sizeof(SharedSynapticInfo)
			 + a_Index * sizeof(T)));
}

//! Direct access to the contents of the memory-mapped file (returns object/array pointer).
/*! 
 * Not thread safe.
 * \return Properly typed pointer to the shared array.
 */
template <class T>
inline
T* Synapse<T>::GrabPointer()
{
	return (reinterpret_cast<T*>(static_cast<char*>(m_pMap) 
		    + sizeof(SharedSynapticInfo)));
}

//! Copy data from passed pointer into memory-mapped file.
/*! 
 * Not thread safe.
 * \param a_pOther Pointer to array or object; will be copied into the shared space.
 * \param a_Size Size of the passed array.
 */
template <class T>
inline
void Synapse<T>::FillPointer(const T* a_pOther, long a_Size)
{
	T* p = GrabPointer();
	for (long i = 0; (i < Size()) && (i < a_Size); i++)
	{
		p[i] = a_pOther[i];
	}
}

//! Get the number of elements that make up the memory-mapped file.
/*! 
 * \return Number of elements, or 1 if single object.
 */
template <class T>
inline
long Synapse<T>::Size()
{
	return m_Size;
}

//---------

//! Grants ownership of the memory-mapped file to the calling thread. Does not wait.
/*!
 * \return TRUE if ownership has been granted, FALSE otherwise.
 * \sa Lock(), Unlock()
 */
template <class T>
inline
bool Synapse<T>::LockNow()
{
	if (WaitForSingleObject(m_hMutex, 0) == WAIT_OBJECT_0)
	{
		m_LockCount++;
		return true;
	}
	return false;
}

//! Grants ownership of the memory-mapped file to the calling thread.
/*!
 * Protects Synapse from simultaneous access by multiple threads.
 * \sa LockNow(), Unlock()
 */
template <class T>
inline
void Synapse<T>::Lock()
{
	WaitForSingleObject(m_hMutex, INFINITE);
	m_LockCount++;
}

//! Waits for all reading of the Synapse to cease; then locks the Synapse.
/*!
 * \return TRUE if the lock could be established; FALSE otherwise (if any waiting was cancelled).
 */
template <class T>
inline
bool Synapse<T>::WriteLock()
{
	bool loop = true;
	do
	{
		Lock();
		if (reinterpret_cast<SharedSynapticInfo*>(m_pMap)->currentReaders == 0)
		{
			return true;
		}
		else
		{
			reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingReaders++;
			Unlock();
			loop = WaitForRead();
			Lock();
			reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingReaders--;
			Unlock();
		}
	}
	while (loop);
	return false;
}

//! Releases ownership of the memory-mapped file.
/*!
 * Needs to be called once for each successful Lock() and LockNow() call.
 * \sa Lock(), LockNow()
 */
template <class T>
inline
void Synapse<T>::Unlock()
{
	ReleaseMutex(m_hMutex);
	m_LockCount--;
}

//---------

//! Wakes up sleeping threads that are waiting for a write event. 
template <class T>
inline
void Synapse<T>::SignalRead() 
{ 
	SetEvent(m_hReadEvent); 
}

//! Wakes up sleeping threads that are waiting for a read event. 
template <class T>
inline
void Synapse<T>::SignalWrite() 
{ 
	SetEvent(m_hWriteEvent); 
}

//! Makes the current thread wait for a read event.
/*!
 * \return FALSE if the wait was cancelled; TRUE otherwise.
 */
template <class T>
inline
bool Synapse<T>::WaitForRead()
{
	Lock();
	if (reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingWriters == 0)
	{
		Unlock();
		WaitForSingleObject(m_hReadEvent, INFINITE);
		Lock();
		if (reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingReaders == 1)
		{
			ResetEvent(m_hReadEvent);
		}
		Unlock();
	}
	else
	{
		Unlock();
	}
	Lock();
	bool stopWaiting = (LocalInfo().flags & SYNAPSE_STOPWAITING) || (SharedInfo().flags & SYNAPSE_STOPWAITING);
	Unlock();
	return !stopWaiting;
}

//! Makes the current thread wait for a write event.
/*!
 * \return FALSE if the wait was cancelled; TRUE otherwise.
 */
template <class T>
inline
bool Synapse<T>::WaitForWrite()
{
	Lock();
	if (reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingReaders == 0)
	{
		Unlock();
		WaitForSingleObject(m_hWriteEvent, INFINITE);
		Lock();
		if (reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingWriters == 1)
		{
			ResetEvent(m_hWriteEvent);
		}
		Unlock();
	}
	else
	{
		Unlock();
	}
	Lock();
	bool stopWaiting = (LocalInfo().flags & SYNAPSE_STOPWAITING) || (SharedInfo().flags & SYNAPSE_STOPWAITING);
	Unlock();
	return !stopWaiting;
}

//! Makes the current thread wait for the Synapse to be updated.
/*!
 * \return FALSE if the wait was cancelled; TRUE otherwise.
 */
template <class T>
inline
bool Synapse<T>::WaitForNew()
{
	if (m_pMap)
	{
		bool loop = true;
		do
		{
			Lock();
			if (_IsNew())
			{
				Unlock();
				return true;
			}
			else
			{
				reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingWriters++;
				Unlock();
				loop = WaitForWrite();
				Lock();
				reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingWriters--;
				Unlock();
			}
		}
		while (loop);
	}
	return false;
}

//! Makes the current thread wait for the Synapse to be read.
/*!
 * \return FALSE if the wait was cancelled; TRUE otherwise.
 */
template <class T>
inline
bool Synapse<T>::WaitForOld()
{
		if (m_pMap)
	{
		bool loop = true;
		do
		{
			Lock();
			if (_IsOld())
			{
				Unlock();
				return true;
			}
			else
			{
				reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingReaders++;
				Unlock();
				loop = WaitForRead();
				Lock();
				reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingReaders--;
				Unlock();
			}
		}
		while (loop);
	}
	return false;
}

//! Waits until passed object matches shared object (does not update access times until it succeeds).
/*!
 * \param a_rObject Reference to the object updated from memory-mapped file.
 * \param a_Index Index of the object in memory-mapped array (if any).
 * \param a_fComparator Pointer to comparison function.
 * \return FALSE if the wait was cancelled; TRUE otherwise.
 */
template <class T>
inline
bool Synapse<T>::WaitForMatch(T a_rObject, long a_Index, bool a_fComparator(const T& a_rTarget, const T& a_rValue))
{
	if (m_pMap)
	{
		bool loop = true;
		do
		{
			T tempObject;
			Lock();
			static_cast<SharedSynapticInfo*>(m_pMap)->currentReaders++;
			Unlock();
			_CopyFromShared(SynapticReference<T>(tempObject, a_Index));
			Lock();
			static_cast<SharedSynapticInfo*>(m_pMap)->currentReaders--;
			SignalRead(); // for currentReaders watch
			reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingWriters++;
			Unlock();
			if (a_fComparator(tempObject, a_rObject))
			{
				Lock();
				reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingWriters--;
				Unlock();
				SetAsRead();
				return true;
			}
			else
			{
				loop = WaitForWrite();
				Lock();
				reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingWriters--;
				Unlock();
			}
		}
		while (loop);
	}
	return false;
}

//! Sets stop variable and wakes up sleeping threads. 
/*!
 * Any ongoing waiting will be cancelled if stop variable is TRUE.
 * \param a_StopValue Set to TRUE to cancel waiting by this Synapse; FALSE to reset.
 * \param a_StopEveryone Set to TRUE to cancel waiting for all sharing Synapses; FALSE to reset.
 */
template <class T>
inline
void Synapse<T>::StopWaiting(bool a_StopValue, bool a_StopEveryone)
{
	if (a_StopEveryone)
	{
		Lock();
		if (a_StopValue)
		{
			SharedInfo().flags |= SYNAPSE_STOPWAITING;
		}
		else
		{
			SharedInfo().flags &= ~SYNAPSE_STOPWAITING;
		}
		Unlock();
	}
	if (a_StopValue)
	{
		LocalInfo().flags |= SYNAPSE_STOPWAITING;
	}
	else
	{
		LocalInfo().flags &= ~SYNAPSE_STOPWAITING;
	}
	SignalRead();
	SignalWrite();
}

//! Check whether ongoing waiting has been forcefully cancelled.
/*!
 * \return TRUE if no waiting is allowed.
 * \sa StopWaiting()
 */
template <class T>
inline
bool Synapse<T>::HasStoppedWaiting()
{
	bool result = false;
	if (LocalInfo().flags & SYNAPSE_STOPWAITING)
	{
		result = true;
	}
	else
	{
		Lock();
		if (SharedInfo().flags & SYNAPSE_STOPWAITING)
		{
			result = true;
		}
		Unlock();
	}
	return result;
}


//---------

//! Checks whether the memory-mapped file has been updated since the last successful read by this Synapse.
/*!
 * \return TRUE if file has been updated by another Synapse; FALSE otherwise.
 */
template <class T>
inline
bool Synapse<T>::IsNew()
{
	if (m_pMap)
	{
		Lock();
		if (_IsNew())
		{
			Unlock();
			return true;
		}
		Unlock();
	}
	return false;
}

//! Checks whether the memory-mapped file has been read since the last successful write by this Synapse.
/*!
 * \return TRUE if file has been read by another Synapse; FALSE otherwise.
 */
template <class T>
inline
bool Synapse<T>::IsOld()
{
	if (m_pMap)
	{
		Lock();
		if (_IsOld())
		{
			Unlock();
			return true;
		}
		Unlock();
	}
	return false;
}

//! Retrieves current system time (in ticks).
/*!
 * \return High-resolution time.
 * \sa OneSecond()
 */
template <class T>
inline
long long Synapse<T>::Time()
{
	LARGE_INTEGER currentTick;
	QueryPerformanceCounter(&currentTick);
	return currentTick.QuadPart;
}

//! Retrieves frequency of ticks (needed to convert the system time to seconds).
/*!
 * \return Ticks per second.
 * \sa Time()
 */
template <class T>
inline
long long Synapse<T>::OneSecond()
{
	LARGE_INTEGER ticksPerSecond;
	QueryPerformanceFrequency(&ticksPerSecond);
	return ticksPerSecond.QuadPart;
}

//! Updates local and shared last read time.
/*!
 * Signals other threads that a read has occurred.
 * \param a_NewTime Desired time value to insert (defaults to current time).
 * \sa UpdateWrite
 */
template <class T>
inline
void Synapse<T>::SetAsRead(long long a_NewTime)
{
	Lock();
	_UpdateReadTime(a_NewTime);
	SignalRead();
	Unlock();
}

//! Updates local and shared last write time.
/*!
 * Signals other threads that a write has occurred.
 * \param a_NewTime Desired time value to insert (defaults to current time).
 * \sa UpdateRead
 */
template <class T>
inline
void Synapse<T>::SetAsWritten(long long a_NewTime)
{
	Lock();
	_UpdateWriteTime(a_NewTime);
	SignalWrite();
	Unlock();
}

//---------

//! Checks whether a memory-mapped file and synchronization mutex exists.
/*!
 * \param a_pShareName Existing name of file and mutex.
 * \return TRUE if named file and mutex exist; FALSE if file and mutex do not exist, 
 * or object size in file is incompatible with the type of this Synapse.
 * \sa Synapse<T>::OpenNow
 */
template <class T>
bool Synapse<T>::Exists(const char* a_pShareName)
{
	if (a_pShareName)
	{
		size_t namelength = strlen(a_pShareName);					
		char* tempname = new char[namelength + 20];
		sprintf_s(tempname, namelength + 20, "%s : mutex", a_pShareName);

		bool retval = false;
		HANDLE hMutex = OpenMutexA(MUTEX_ALL_ACCESS, FALSE, tempname);
		if (hMutex)
		{
			sprintf_s(tempname, namelength + 20, "%s : map", a_pShareName);
			HANDLE hMapFile = OpenFileMappingA(
								FILE_MAP_READ,  // read access only
								FALSE,          // do not inherit the name
								tempname);		// name of mapping object 
			if (hMapFile)
			{
				void* pMap = MapViewOfFile(hMapFile,	// handle to map object
									FILE_MAP_READ,		// read permission
									0,                   
									0,                   
									sizeof(SharedSynapticInfo));
				if (pMap)
				{
					long unitSize = reinterpret_cast<SharedSynapticInfo*>(pMap)->unitSize;
					UnmapViewOfFile(pMap);
					if (unitSize == sizeof(T))
					{
						retval = true;
					}
				}
				CloseHandle(hMapFile);
			}
			CloseHandle(hMutex);
			hMutex = 0;
		}
		delete [] tempname;
		return retval;
	}
	return false;
}

//! Creates a new memory-mapped file and synchronization mutex.
/*!
 * \param a_pShareName Desired name of file and mutex.
 * \param a_Size Number of instances of object to store in file.
 * \param a_rDefaultObject Default object data to copy into file.
 * \return TRUE if file and mutex were created successfully; FALSE if file and mutex already exist,
 * name is already used by the system, or memory allocation problems have occurred.
 */
template <class T>
bool Synapse<T>::Create(const char* a_pShareName, long a_Size, const T& a_rDefaultObject)
{
	return _Create(a_pShareName, SynapticReference<T>(a_rDefaultObject, a_Size));
}

//! Creates a new memory-mapped file and synchronization mutex, and initialize to passed C-style array.
/*!
 * \param a_pShareName Desired name of file and mutex.
 * \param a_Size Number of instances of object to store in file.
 * \param a_pArrayIn Initial array data to copy into file.
 * \return TRUE if file and mutex were created successfully; FALSE if file and mutex already exist,
 * name is already used by the system, or memory allocation problems have occurred.
 */
template <class T>
bool Synapse<T>::CreateFromArray(const char* a_pShareName, long a_Size, const T a_pArrayIn[])
{
	return _Create(a_pShareName, SynapticReference<T>(a_pArrayIn, a_Size));
}

//! Opens an existing memory-mapped file and synchronization mutex.
/*!
 * \param a_pShareName Existing name of file and mutex.
 * \return TRUE if named file and mutex exist; FALSE if file and mutex do not exist, 
 * object size in file is incompatible with the type of this Synapse, or Synapse already open.
 */
template <class T>
bool Synapse<T>::OpenNow(const char* a_pShareName)
{
	if (!m_hMapFile && a_pShareName)
	{
		size_t namelength = strlen(a_pShareName);					
		char* tempname = new char[namelength + 20];
		sprintf_s(tempname, namelength + 20, "%s : mutex", a_pShareName);
		m_hMutex = OpenMutexA(MUTEX_ALL_ACCESS, FALSE, tempname);
		if (m_hMutex)
		{
			sprintf_s(tempname, namelength + 20, "%s : event : read", a_pShareName);
			m_hReadEvent = OpenEventA(EVENT_ALL_ACCESS, FALSE, tempname);
			sprintf_s(tempname, namelength + 20, "%s : event : write", a_pShareName);
			m_hWriteEvent = OpenEventA(EVENT_ALL_ACCESS, FALSE, tempname);
			if (m_hReadEvent && m_hWriteEvent)
			{
				sprintf_s(tempname, namelength + 20, "%s : map", a_pShareName);
				m_hMapFile = OpenFileMappingA(
								FILE_MAP_ALL_ACCESS,   // read/write access
								FALSE,              // do not inherit the name
								tempname);		// name of mapping object 
				if (m_hMapFile)
				{
					m_pMap = MapViewOfFile(m_hMapFile,		// handle to map object
									FILE_MAP_READ,	// read permission
									0,                   
									0,                   
									sizeof(SharedSynapticInfo));
					if (m_pMap)
					{
						m_Size = reinterpret_cast<SharedSynapticInfo*>(m_pMap)->arraySize;
						long unitSize = reinterpret_cast<SharedSynapticInfo*>(m_pMap)->unitSize;
						UnmapViewOfFile(m_pMap);
						m_pMap = NULL;
						if (unitSize == sizeof(T))
						{
							m_pMap = MapViewOfFile(m_hMapFile,		
										FILE_MAP_ALL_ACCESS,	// read/write permission
										0,                   
										0,                   
										sizeof(SharedSynapticInfo) + m_Size * sizeof(T));
							if (m_pMap)
							{
								delete [] tempname;
								m_pShareName = new char[namelength+1];
								strcpy_s(m_pShareName, namelength+1, a_pShareName);
								return true;
							}
						}
					}
					CloseHandle(m_hMapFile);
				}
				CloseHandle (m_hReadEvent);
				CloseHandle (m_hWriteEvent);
				m_hReadEvent = 0;
				m_hWriteEvent = 0;
			}
			CloseHandle(m_hMutex);
			m_hMutex = 0;
		}
		delete [] tempname;
		m_Size = 0;
	}
	return false;
}

//! Opens an existing memory-mapped file and synchronization mutex. Waits if none is available yet.
/*!
 * \param a_pShareName Existing name of file and mutex.
 * \param a_PollWaitTime Time to wait between checking for availability (in milliseconds).
 */
template <class T>
bool Synapse<T>::Open(const char* a_pShareName, long a_PollWaitTime)
{
	if (!a_pShareName) return false;
	while (!OpenNow(a_pShareName))
	{
		Sleep(a_PollWaitTime);
		if (LocalInfo().flags & SYNAPSE_STOPWAITING) return false;
	}
	return true;
}

//! Closes the memory-mapped file and synchronization mutex.
template <class T>
void Synapse<T>::Release()
{
	delete [] m_pShareName;
	m_pShareName = NULL;
	m_Size = 0;
	UnmapViewOfFile(m_pMap);
	m_pMap = NULL;
	CloseHandle(m_hMapFile);
	m_hMapFile = 0;
	while (m_LockCount)
	{
		Unlock();
	}
	CloseHandle(m_hReadEvent);
	m_hReadEvent = 0;
	CloseHandle(m_hWriteEvent);
	m_hWriteEvent = 0;
	CloseHandle(m_hMutex);
	m_hMutex = 0;
}

//! Checks whether creation/opening of file/mutex succeeded and Synapse is ready to be used.
/*! \return TRUE if successful, FALSE otherwise.
 */
template <class T>
inline
bool Synapse<T>::IsOpen() const
{ 
	return m_pMap != NULL; 
}

//! Retrieves the shared name of the created or opened mutex/file.
/*! \return Current name. Empty if file/mutex creation/opening failed.
 */
template <class T>
inline
const char* Synapse<T>::GetName() const 
{ 
	return m_pShareName; 
}

//---------

//! Updates memory-mapped file to the passed object, if file can be accessed immediately.
/*!
 * Thread safe. Fails if another thread is currently reading from or writing to the same file.
 * Does not allow other threads to read or write at the same time.
 * \param a_rObject Reference to the new state of the object.
 * \param a_Index Target index of the object in memory-mapped array (if any).
 * \return TRUE if file has been updated; FALSE otherwise.
 */
template <class T>
inline
bool Synapse<T>::WriteNow(const T& a_rObject, long a_Index)
{
	if (m_pMap)
	{
		return _WriteNow(SynapticReference<T>(a_rObject, a_Index));
	}
	return false;
}

//! Reads memory-mapped file into the passed object, if file can be accessed immediately.
/*!
 * Thread safe. Fails if another thread is currently writing to the same file.
 * Does not allow other threads to read or write at the same time.
 * \param a_rObject Reference to the object updated from memory-mapped file.
 * \param a_Index Index of the object in memory-mapped array (if any).
 * \return TRUE if file has been read; FALSE otherwise.
 */
template <class T>
inline
bool Synapse<T>::ReadNow(T& a_rObject, long a_Index)
{
	if (m_pMap)
	{
		return _ReadNow(SynapticReference<T>(a_rObject, a_Index));
	}
	return false;
}

//! Return object copy of memory-mapped file, if file can be accessed immediately.
/*!
 * Thread safe. Fails if another thread is currently writing to the same file.
 * Does not allow other threads to read or write at the same time.
 * \param a_Index Index of the object in memory-mapped array (if any).
 * \return Object copy if access to file is possible; empty object otherwise.
 * \sa Synapse<T>::ReadNow
 */
template <class T>
inline
T Synapse<T>::ReadCopyNow(long a_Index)
{
	if (m_pMap)
	{
		T tempObject;
		if (_ReadNow(SynapticReference<T>(tempObject, a_Index)))
			return tempObject;
	}
	return T();
}

//! Updates memory-mapped file to the passed object. Waits if necessary.
/*!
 * Thread safe. Waits for another thread to finish reading from or writing to the same file.
 * Does not allow other threads to read or write at the same time.
 * \param a_rObject Reference to the new state of the object.
 * \param a_Index Target index of the object in memory-mapped array (if any).
 */
template <class T>
inline
bool Synapse<T>::Write(const T& a_rObject, long a_Index)
{
	if (m_pMap)
	{
		return _Write(SynapticReference<T>(a_rObject, a_Index));
	}
	return false;
}

//! Updates memory-mapped file (waits if necessary); then waits for the update to be read before returning.
/*!
 * Thread safe. Waits for another thread to finish reading from or writing to the same file.
 * Does not allow other threads to read or write at the same time.
 * \param a_rObject Reference to the new state of the object.
 * \param a_Index Target index of the object in memory-mapped array (if any).
 * \return TRUE if the operation was successful; FALSE otherwise.
 */
template <class T>
inline
bool Synapse<T>::ProtectWrite(const T& a_rObject, long a_Index)
{
	if (m_pMap)
	{
		return _ProtectWrite(SynapticReference<T>(a_rObject, a_Index));
	}
	return false;
}

//! Reads memory-mapped file into the passed object. Waits if necessary.
/*!
 * Thread safe. Waits for another thread to finish writing to the same file.
 * Allows other threads to read at the same time.
 * \param a_rObject Reference to the object updated from memory-mapped file.
 * \param a_Index Index of the object in memory-mapped array (if any).
 * \return TRUE if the operation was successful; FALSE otherwise.
 */
template <class T>
inline
bool Synapse<T>::Read(T& a_rObject, long a_Index)
{
	if (m_pMap)
	{
		_Read(SynapticReference<T>(a_rObject, a_Index));
		return true;
	}
	return false;
}

//! Return object copy of memory-mapped file. Waits if necessary.
/*!
 * Thread safe. Waits for another thread to finish writing to the same file.
 * Allows other threads to read at the same time.
 * \param a_Index Index of the object in memory-mapped array (if any).
 * \return Object copy.
 * \sa Synapse<T>::Read
 */
template <class T>
inline
T Synapse<T>::ReadCopy(long a_Index)
{
	if (m_pMap)
	{
		T tempObject;
		_Read(SynapticReference<T>(tempObject, a_Index));
		return tempObject;
	}
	return T();
}

//! Updates memory-mapped file to the passed object, if the previous file state has already been read by this or another Synapse.
/*!
 * Thread safe. Waits for other threads to finish. Waits for shared read time to exceed local write time.
 * Does not allow other threads to read or write at the same time.
 * \param a_rObject Reference to the new state of the object.
 * \param a_Index Target index of the object in memory-mapped array (if any).
 * \return TRUE if the operation was successful; FALSE otherwise.
 * \sa Write()
 */
template <class T>
inline
bool Synapse<T>::WriteWhenOld(const T& a_rObject, long a_Index)
{
	if (m_pMap)
	{
		return _WriteWhenOld(SynapticReference<T>(a_rObject, a_Index));
	}
	return false;
}

//! Reads memory-mapped file into the passed object, if the file state is newer than what was last read by this Synapse.
/*!
 * Thread safe. Waits for other threads to finish. Waits for shared write time to exceed local read time.
 * Allows other threads to read at the same time.
 * \param a_rObject Reference to the object updated from memory-mapped file.
 * \param a_Index Index of the object in memory-mapped array (if any).
 * \return TRUE if the operation was successful; FALSE otherwise.
 */
template <class T>
inline
bool Synapse<T>::ReadWhenNew(T& a_rObject, long a_Index)
{
	if (m_pMap)
	{
		return _ReadWhenNew(SynapticReference<T>(a_rObject, a_Index));
	}
	return false;
}

//! Return object copy of memory-mapped file, if the file state is newer than what was last read by this Synapse.
/*!
 * Thread safe. Waits for other threads to finish. Waits for shared write time to exceed local read time.
 * Allows other threads to read at the same time.
 * \param a_Index Index of the object in memory-mapped array (if any).
 * \return Object copy.
 * \sa Synapse<T>::ReadWhenNew
 */
template <class T>
inline
T Synapse<T>::ReadCopyWhenNew(long a_Index)
{
	if (m_pMap)
	{
		T tempObject;
		_ReadWhenNew(SynapticReference<T>(tempObject, a_Index));
		return tempObject;
	}
	return T();
}

//---------

//! Updates memory-mapped file to the passed array, if file can be accessed immediately.
/*!
 * \param a_pArrayIn Pointer to array that updates file.
 * \param a_Size Size of passed array.
 * \return TRUE if file has been updated; FALSE otherwise.
 * \sa Synapse<T>::WriteNow
 */
template <class T>
inline
bool Synapse<T>::WriteArrayNow(const T a_pArrayIn[], long a_Size)
{
	if (m_pMap)
	{
		return _WriteNow(SynapticReference<T>(a_pArrayIn, a_Size));
	}
	return false;
}

//! Reads memory-mapped file into the passed array, if file can be accessed immediately.
/*!
 * \param a_pArrayOut Pointer to array which is updated by file data.
 * \param a_Size Size of passed array.
 * \return TRUE if file has been updated; FALSE otherwise.
 * \sa Synapse<T>::ReadNow
 */
template <class T>
inline
bool Synapse<T>::ReadArrayNow(T a_pArrayOut[], long a_Size)
{
	if (m_pMap)
	{
		return _ReadNow(SynapticReference<T>(a_pArrayOut, a_Size));
	}
	return false;
}

//! Updates memory-mapped file to the passed array. Waits if necessary.
/*!
 * \param a_pArrayIn Pointer to array that updates file.
 * \param a_Size Size of passed array.
 * \return TRUE if the operation was successful; FALSE otherwise.
 * \sa Synapse<T>::Write
 */
template <class T>
inline
bool Synapse<T>::WriteArray(const T a_pArrayIn[], long a_Size)
{
	if (m_pMap)
	{
		return _Write(SynapticReference<T>(a_pArrayIn, a_Size));
	}
	return false;
}

//! Updates memory-mapped file (waits if necessary); then waits for the update to be read before returning.
/*!
 * \param a_pArrayIn Pointer to array that updates file.
 * \param a_Size Size of passed array.
 * \return TRUE if the operation was successful; FALSE otherwise.
 * \sa Synapse<T>::Write
 */
template <class T>
inline
bool Synapse<T>::ProtectWriteArray(const T a_pArrayIn[], long a_Size)
{
	if (m_pMap)
	{
		return _ProtectWrite(SynapticReference<T>(a_pArrayIn, a_Size));
	}
	return false;
}

//! Reads memory-mapped file into the passed array. Waits if necessary.
/*!
 * \param a_pArrayOut Pointer to array which is updated by file data.
 * \param a_Size Size of passed array.
 * \return TRUE if the operation was successful; FALSE otherwise.
 * \sa Synapse<T>::Read
 */
template <class T>
inline
bool Synapse<T>::ReadArray(T a_pArrayOut[], long a_Size)
{
	if (m_pMap)
	{
		_Read(SynapticReference<T>(a_pArrayOut, a_Size));
		return true;
	}
	return false;
}

//! Updates memory-mapped file to the passed array, if the previous file state has already been read by this or another Synapse.
/*!
 * \param a_pArrayIn Pointer to array that updates file.
 * \param a_Size Size of passed array.
 * \return TRUE if the operation was successful; FALSE otherwise.
 * \sa Synapse<T>::WriteWhenOld
 */
template <class T>
inline
bool Synapse<T>::WriteArrayWhenOld(const T a_pArrayIn[], long a_Size)
{
	if (m_pMap)
	{
		return _WriteWhenOld(SynapticReference<T>(a_pArrayIn, a_Size));
	}
	return false;
}

//! Reads memory-mapped file into the passed array, if the file state is newer than what was last read by this Synapse.
/*!
 * \param a_pArrayOut Pointer to array which is updated by file data.
 * \param a_Size Size of passed array.
 * \return TRUE if the operation was successful; FALSE otherwise.
 * \sa Synapse<T>::ReadWhenNew
 */
template <class T>
inline
bool Synapse<T>::ReadArrayWhenNew(T a_pArrayOut[], long a_Size)
{
	if (m_pMap)
	{
		return _ReadWhenNew(SynapticReference<T>(a_pArrayOut, a_Size));
	}
	return false;
}

//---------

//! Returns parameter object for local Synapse instance.
/*!
 * \return Modifiable object of parameter values.
 * \sa SynapticInfo
 */
template <class T>
inline
SynapticInfo& Synapse<T>::LocalInfo()
{
	return m_Info;
}

//! Returns parameter object for shared/remote Synapse instance.
/*!
 * You cannot modify these parameters directly.
 * \return Constant object of parameter values.
 * \sa SharedSynapticInfo
 */
template <class T>
inline
SharedSynapticInfo& Synapse<T>::SharedInfo()
{
	Lock();
	SharedSynapticInfo* pShared = static_cast<SharedSynapticInfo*>(m_pMap);
	Unlock();
	return *pShared;
}

//---------

template <class T>
bool Synapse<T>::_Create(const char* a_pShareName, const SynapticReference<T>& a_rSR)
{
	if (!m_hMapFile && a_pShareName)
	{
		size_t namelength = strlen(a_pShareName);		
		char* tempname = new char[namelength + 20];
		sprintf_s(tempname, namelength + 20, "%s : mutex", a_pShareName);

		m_hMutex = CreateMutexA(NULL, TRUE, tempname);
		if (m_hMutex)
		{
			sprintf_s(tempname, namelength + 20, "%s : event : read", a_pShareName);
			m_hReadEvent = CreateEventA(NULL, TRUE, FALSE, tempname);
			sprintf_s(tempname, namelength + 20, "%s : event : write", a_pShareName);
			m_hWriteEvent = CreateEventA(NULL, TRUE, FALSE, tempname);
			if (m_hReadEvent && m_hWriteEvent)
			{
				sprintf_s(tempname, namelength + 20, "%s : map", a_pShareName);
				m_hMapFile = CreateFileMappingA(
								INVALID_HANDLE_VALUE,
								NULL,
								PAGE_READWRITE,
								0,
								(DWORD)(sizeof(SharedSynapticInfo) + a_rSR.number * sizeof(T)),
								tempname);
				if ((m_hMapFile) && (GetLastError() != ERROR_ALREADY_EXISTS))
				{
					m_pMap = MapViewOfFile(m_hMapFile,		// handle to map object
									FILE_MAP_ALL_ACCESS,	// read/write permission
									0,                   
									0,                   
									sizeof(SharedSynapticInfo) + a_rSR.number * sizeof(T));
					if (m_pMap) 
					{
						SharedSynapticInfo defaultinfo;
						defaultinfo.arraySize = a_rSR.number;
						defaultinfo.unitSize = sizeof(T);
						bool copy_ok = memcpy(m_pMap, &defaultinfo, sizeof(SharedSynapticInfo)) != NULL;
						switch (a_rSR.type)
						{
							case 1:
								for (long i = 0; copy_ok && (i < a_rSR.number); i++)
								{
									copy_ok &= memcpy(static_cast<char*>(m_pMap) + sizeof(SharedSynapticInfo) + i * sizeof(T), a_rSR.pData, sizeof(T)) != NULL;
								}
								break;
							case 2:
								if (copy_ok && a_rSR.pData)
									copy_ok = memcpy(static_cast<char*>(m_pMap) + sizeof(SharedSynapticInfo), a_rSR.pData, a_rSR.number * sizeof(T)) != NULL;
								else
									copy_ok = false;
								break;
						}
						if (copy_ok)	
						{
							ReleaseMutex(m_hMutex);
							delete [] tempname;
							m_Size = a_rSR.number;
							m_pShareName = new char[namelength+1];
							strcpy_s(m_pShareName, namelength+1, a_pShareName);
							return true;
						}
					}
					UnmapViewOfFile(m_pMap);
					m_pMap = NULL;
					CloseHandle(m_hMapFile);
				}
				CloseHandle(m_hReadEvent);
				CloseHandle(m_hWriteEvent);
				m_hReadEvent = 0;
				m_hWriteEvent = 0;
			}
			ReleaseMutex(m_hMutex);
			CloseHandle(m_hMutex);
			m_hMutex = 0;
		}
		delete [] tempname;
	}
	return false;
}

//---------

template <class T>
inline
bool Synapse<T>::_WriteNow(const SynapticReference<T>& a_rSR)
{
	if (LockNow())
	{
		if (static_cast<SharedSynapticInfo*>(m_pMap)->currentReaders == 0)
		{
			_CopyToShared(a_rSR);
			_UpdateWriteTime(Time());
			SignalWrite();
			Unlock();
			return true;
		}
		Unlock();
	}
	return false;
}

template <class T>
inline
bool Synapse<T>::_ReadNow(const SynapticReference<T>& a_rSR)
{
	if (LockNow())
	{
		_UpdateReadTime(Time());
		_CopyFromShared(a_rSR);
		SignalRead();
		Unlock();
		return true;
	}
	return false;
}

template <class T>
inline
bool Synapse<T>::_Write(const SynapticReference<T>& a_rSR)
{
	if (WriteLock())
	{
		_CopyToShared(a_rSR);
		_UpdateWriteTime(Time());
		SignalWrite();
		Unlock();
		return true;
	}
	return false;
}

template <class T>
inline
bool Synapse<T>::_ProtectWrite(const SynapticReference<T>& a_rSR)
{
	long long now = 0;
	if (WriteLock())
	{
		_CopyToShared(a_rSR);
		now = Time();
		_UpdateWriteTime(now);
		static_cast<SharedSynapticInfo*>(m_pMap)->currentReaders++;
		SignalWrite();
		Unlock();
		bool loop = true;	
		do 
		{
			Lock();
			if (static_cast<SharedSynapticInfo*>(m_pMap)->lastReadTime > now)
			{
				static_cast<SharedSynapticInfo*>(m_pMap)->currentReaders--;
				SignalRead(); // for currentReaders watch
				Unlock();
				return true;
			}
			else
			{
				reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingReaders++;
				Unlock();
				loop = WaitForRead();
				Lock();
				reinterpret_cast<SharedSynapticInfo*>(m_pMap)->waitingReaders--;
				Unlock();
				
			}
		} 
		while (loop);
	}
	return false;
}

template <class T>
inline
void Synapse<T>::_Read(const SynapticReference<T>& a_rSR)
{
	Lock();
	static_cast<SharedSynapticInfo*>(m_pMap)->currentReaders++;
	Unlock();
	_CopyFromShared(a_rSR);
	Lock();
	_UpdateReadTime(Time());
	static_cast<SharedSynapticInfo*>(m_pMap)->currentReaders--;
	SignalRead();
	Unlock();
}

template <class T>
inline
bool Synapse<T>::_WriteWhenOld(const SynapticReference<T>& a_rSR)
{
	if (WaitForOld())
	{
		return _Write(a_rSR);
	}
	return false;
}

template <class T>
inline
bool Synapse<T>::_ReadWhenNew(const SynapticReference<T>& a_rSR)
{
	if (WaitForNew())
	{
		_Read(a_rSR);
		return true;
	}
	return false;
}

template <class T>
inline
void Synapse<T>::_CopyToShared(const SynapticReference<T>& a_rSR)
{
	switch (a_rSR.type)
	{
		case 1:
			Grab(a_rSR.number) = *(a_rSR.pData);
			break;
		case 2:
			{
				T* p = GrabPointer();
				for (long i = 0; (i < Size()) && (i < a_rSR.number); i++)
				{
					p[i] = a_rSR.pData[i];
				}
			}
			break;
	}
}

template <class T>
inline
void Synapse<T>::_CopyFromShared(const SynapticReference<T>& a_rSR)
{
	switch (a_rSR.type)
	{
		case 1:
			*(a_rSR.pData) = Grab(a_rSR.number);
			break;
		case 2:
			{
				T* p = GrabPointer();
				for (long i = 0; (i < Size()) && (i < a_rSR.number); i++)
				{
					a_rSR.pData[i] = p[i];
				}
			}
			break;
	}
}

template <class T>
inline
void Synapse<T>::_UpdateReadTime(long long a_NewTime)
{
	static_cast<SharedSynapticInfo*>(m_pMap)->lastReadTime = LocalInfo().lastReadTime = a_NewTime;
}

template <class T>
inline
void Synapse<T>::_UpdateWriteTime(long long a_NewTime)
{
	static_cast<SharedSynapticInfo*>(m_pMap)->lastWriteTime = LocalInfo().lastWriteTime = a_NewTime;
	LocalInfo().lastReadTime = a_NewTime;				
}

template <class T>
inline
bool Synapse<T>::_IsNew()
{
	return static_cast<SharedSynapticInfo*>(m_pMap)->lastWriteTime > LocalInfo().lastReadTime;
}

template <class T>
inline
bool Synapse<T>::_IsOld()
{
	return static_cast<SharedSynapticInfo*>(m_pMap)->lastReadTime >= LocalInfo().lastWriteTime;
}

//-----------------------------------------------------------------------------

#endif
